package chain;

import net.sourceforge.peertwitter.chain.EventChain;
import net.sourceforge.peertwitter.chain.EventChainImpl;
import net.sourceforge.peertwitter.chain.exceptions.EventChainInfiniteLoopException;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;

import rice.p2p.commonapi.Message;

@SuppressWarnings("unchecked")
public class EventChainTest {

	private Mockery context;
	private EventChain <Message>root;
	private Message message;
	
	@Before
	public void createRoot() {
		context = new Mockery();
		root = new EventChainImpl<Message>();
		message = context.mock(Message.class);
	}
	
	@Test
	public void aMessageChainMustPassItOnToItsChild() {
		final EventChain <Message> child = (EventChain <Message>) context.mock(EventChain.class);
		root.addChild(child);
		
		context.checking(new Expectations() {
			{
				one(child).deliver(message);
			}
		});
		
		root.deliver(message);
		context.assertIsSatisfied();
	}
	
	@Test
	public void aMessageChainMustPassItOnToAllOfItsChildren() {
		final EventChain <Message> child1 = context.mock(EventChain.class,"child1");
		final EventChain <Message> child2 = context.mock(EventChain.class, "child2");
		final EventChain <Message> child3 = context.mock(EventChain.class, "child3");
		
		root.addChild(child1);
		root.addChild(child2);
		root.addChild(child3);
		
		context.checking(new Expectations() {
			{
				one(child1).deliver(message);
				one(child2).deliver(message);
				one(child3).deliver(message);
			}
		});
		
		root.deliver(message);
		context.assertIsSatisfied();
	}
	
	@Test
	public void aMessageReachesAChildOnlyOnceEvenIfItWasMoreThanOnceRegistered() {
		final EventChain <Message> child = context.mock(EventChain.class);
		
		root.addChild(child);
		root.addChild(child);
		
		context.checking(new Expectations() {
			{
				oneOf(child).deliver(message);
			}
		});
		
		root.deliver(message);
		context.assertIsSatisfied();
	}
	
	@Test(expected=EventChainInfiniteLoopException.class)
	public void aRootCantAddItselfAsChildAndThrowsChainInfiniteLoopException() {
		root.addChild(root);
	}
	
	@Test
	public void aMessageReachesAGrandSon() {
		EventChain <Message> child = new EventChainImpl<Message>();
		final EventChain <Message> grandSon = (EventChain<Message>)context.mock(EventChain.class);
		
		root.addChild(child);
		child.addChild(grandSon);
		
		context.checking(new Expectations() {
			{
				oneOf(grandSon).deliver(message);
			}
		});
		
		root.deliver(message);
		context.assertIsSatisfied();
	}
	
	@Test
	public void aMessageReachesGrandsons() {
		EventChain <Message> child = new EventChainImpl<Message>();
		final EventChain <Message> grandSon1 = context.mock(EventChain.class,"grandson1");
		final EventChain <Message> grandSon2 = context.mock(EventChain.class,"grandson2");
		final EventChain <Message> grandSon3 = context.mock(EventChain.class,"grandson3");
		
		root.addChild(child);
		child.addChild(grandSon1);
		child.addChild(grandSon2);
		child.addChild(grandSon3);
		
		context.checking(new Expectations() {
			{
				oneOf(grandSon1).deliver(message);
				oneOf(grandSon2).deliver(message);
				oneOf(grandSon3).deliver(message);
			}
		});
		
		root.deliver(message);
		context.assertIsSatisfied();
	}
	
	@Test
	public void aMessageReachesGrandsonsOfSeveralChilds() {
		EventChain <Message> childWithNoKids = new EventChainImpl <Message>();
		EventChain <Message> childWithOneKid = new EventChainImpl<Message>();
		EventChain <Message> childWithThreeKidsAndOneGrandSon = new EventChainImpl <Message>();
		
		final EventChain <Message> grandSon1 = context.mock(EventChain.class,"grandson1");
		final EventChain <Message> grandSon2 = context.mock(EventChain.class,"grandson2");
		final EventChain <Message> grandSon3 = context.mock(EventChain.class,"grandson3");
		final EventChain <Message> grandSonWithSon = new EventChainImpl<Message>();
		
		final EventChain<Message> grandGrandSon = context.mock(EventChain.class,"grandGrandSon");
		
		root.addChild(childWithNoKids);
		root.addChild(childWithOneKid);
		root.addChild(childWithThreeKidsAndOneGrandSon);
	
		childWithOneKid.addChild(grandSon1);
		
		childWithThreeKidsAndOneGrandSon.addChild(grandSon2);
		childWithThreeKidsAndOneGrandSon.addChild(grandSon3);
		childWithThreeKidsAndOneGrandSon.addChild(grandSonWithSon);
		
		grandSonWithSon.addChild(grandGrandSon);
		
		context.checking(new Expectations() {
			{
				oneOf(grandSon1).deliver(message);
				oneOf(grandSon2).deliver(message);
				oneOf(grandSon3).deliver(message);
				oneOf(grandGrandSon).deliver(message);
			}
		});
		
		root.deliver(message);
		context.assertIsSatisfied();
	}
	
	
}
